﻿#define PKSTACKPANEL_DEBUG

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;

namespace pklib.Controls {
	public class StackPanel : System.Windows.Controls.StackPanel {

#if PKSTACKPANEL_DEBUG
		const string DebugNodeName = "zzz";
		bool InDebugNode = false;
#endif

		public override void EndInit() {
			if (this.Orientation == Orientation.Horizontal)
				this.VerticalAlignment = VerticalAlignment.Top;
			base.EndInit();
		}

		protected override System.Windows.Size ArrangeOverride(System.Windows.Size ArrangeSize) {
			System.Windows.Size ret;

			#if PKSTACKPANEL_DEBUG
				if (this.Name == DebugNodeName)
					this.InDebugNode = true;
			#endif
			if(this.Orientation == Orientation.Horizontal)
				ret = this.ArrangeHorizontally(ArrangeSize);
			else
				ret = this.ArrangeVertically(ArrangeSize);
			#if PKSTACKPANEL_DEBUG
				if (this.Name == DebugNodeName)
					this.InDebugNode = false;
			#endif
			return ret;
		}

		private System.Windows.Size ArrangeVertically(Size ArrangeSize) {
			#if PKSTACKPANEL_DEBUG
				if (this.InDebugNode) {
					Debug.WriteLine(String.Format("ArrangeVertically({0}): ArrangeSize: {1:#} x {2:#}", this.Name, ArrangeSize.Width, ArrangeSize.Height));
					Debug.Indent();
				}
			#endif

			List<FrameworkElement> Stretchers = new List<FrameworkElement>();
			double y = 0;
			double NonStretchersHeight = 0;

			foreach (FrameworkElement Child in this.InternalChildren) {
				if ((VerticalAlignment)Child.GetValue(VerticalAlignmentProperty) == VerticalAlignment.Stretch)
					Stretchers.Add(Child);
				else
					NonStretchersHeight += Child.DesiredSize.Height;
			}
			#if PKSTACKPANEL_DEBUG
				if (this.InDebugNode) {
					Debug.WriteLine(String.Format("Stretchers: {0}, NonStretchersHeight: {1:#}", Stretchers.Count, NonStretchersHeight));
				}
			#endif

			foreach (FrameworkElement Child in this.InternalChildren) {
				Size DesiredSize = Child.DesiredSize;
				Rect Position = new Rect(0, y, Child.DesiredSize.Width, Child.DesiredSize.Height);

				#if PKSTACKPANEL_DEBUG
					if (this.InDebugNode) {
						Debug.WriteLine(String.Format("{0}: HorizAlign: {1}, VertAlign: {2} .DesiredSize: {3:#} x {4:#}", Child.Name, (HorizontalAlignment)Child.GetValue(HorizontalAlignmentProperty), (VerticalAlignment)Child.GetValue(VerticalAlignmentProperty), DesiredSize.Width, DesiredSize.Height));
					}
				#endif

				if ((VerticalAlignment)Child.GetValue(VerticalAlignmentProperty) == VerticalAlignment.Stretch)
					Position.Height = (ArrangeSize.Height - NonStretchersHeight) / Stretchers.Count;

				switch ((HorizontalAlignment)Child.GetValue(HorizontalAlignmentProperty)) {
					case HorizontalAlignment.Right:
						Position.X = ArrangeSize.Width - Position.Width;
						break;
					case HorizontalAlignment.Center:
						Position.X = (ArrangeSize.Width / 2) - (Position.Width / 2);
						break;
					case HorizontalAlignment.Stretch:
						Position.Width = ArrangeSize.Width;
						break;

				}
				#if PKSTACKPANEL_DEBUG
					if (this.InDebugNode) {
						Debug.WriteLine(String.Format("{0}.Arrange(t:{1:0}, l:{2:0}, w:{3:0}, h:{4:0})", Child.Name, Position.Top, Position.Left, Position.Width, Position.Height));
					}
				#endif
				Child.Arrange(Position);
				y += Position.Height;
			}
			#if PKSTACKPANEL_DEBUG
				if (this.InDebugNode) {
					Debug.Unindent();
					Debug.WriteLine(String.Format("~ArrangeVertically({0}): ArrangeSize: {1:0} x {2:0}", this.Name, ArrangeSize.Width, ArrangeSize.Height));
				}
			#endif

			return ArrangeSize;
		}

		private System.Windows.Size ArrangeHorizontally(Size ArrangeSize) {
			#if PKSTACKPANEL_DEBUG
				if (this.InDebugNode) {
					Debug.WriteLine(String.Format("ArrangeHorizontally({0}): ArrangeSize: {1:#} x {2:#}", this.Name, ArrangeSize.Width, ArrangeSize.Height));
					Debug.Indent();
				}
			#endif

			List<FrameworkElement> Stretchers = new List<FrameworkElement>();
			double x = 0;
			double NonStretchersWidth = 0;

			foreach (FrameworkElement Child in this.InternalChildren) {
				if ((HorizontalAlignment)Child.GetValue(HorizontalAlignmentProperty) == HorizontalAlignment.Stretch)
					Stretchers.Add(Child);
				else
					NonStretchersWidth += Child.DesiredSize.Width;
			}
			#if PKSTACKPANEL_DEBUG
				if (this.InDebugNode) {
					Debug.WriteLine(String.Format("Stretchers: {0}, NonStretchersWidth: {1:#}", Stretchers.Count, NonStretchersWidth));
				}
			#endif

			foreach (FrameworkElement Child in this.InternalChildren) {
				Size DesiredSize = Child.DesiredSize;
				Rect Position = new Rect(x, 0, Child.DesiredSize.Width, Child.DesiredSize.Height);

				#if PKSTACKPANEL_DEBUG
					if (this.InDebugNode) {
						Debug.WriteLine(String.Format("{0}: HorizAlign: {1}, VertAlign: {2} .DesiredSize: {3:#} x {4:#}", Child.Name, (HorizontalAlignment)Child.GetValue(HorizontalAlignmentProperty), (VerticalAlignment)Child.GetValue(VerticalAlignmentProperty), DesiredSize.Width, DesiredSize.Height));
					}
				#endif

				if ((HorizontalAlignment)Child.GetValue(HorizontalAlignmentProperty) == HorizontalAlignment.Stretch)
					Position.Width = (ArrangeSize.Width - NonStretchersWidth) / Stretchers.Count;

				switch((VerticalAlignment)Child.GetValue(VerticalAlignmentProperty)) {
					case VerticalAlignment.Center:
						Position.Y = (ArrangeSize.Height / 2) - (Position.Height / 2);
						break;
					case VerticalAlignment.Bottom:
						Position.Y = ArrangeSize.Height - Position.Height;
						break;
					case VerticalAlignment.Stretch:
						Position.Height = ArrangeSize.Height;
						break;
				}

				#if PKSTACKPANEL_DEBUG
					if (this.InDebugNode) {
						Debug.WriteLine(String.Format("{0}.Arrange(t:{1:0}, l:{2:0}, w:{3:0}, h:{4:0})", Child.Name, Position.Top, Position.Left, Position.Width, Position.Height));
					}
				#endif
				Child.Arrange(Position);
				x += Position.Width;
			}
			#if PKSTACKPANEL_DEBUG
				if (this.InDebugNode) {
					Debug.Unindent();
					Debug.WriteLine(String.Format("~ArrangeHorizontally({0}): ArrangeSize: {1:0} x {2:0}", this.Name, ArrangeSize.Width, ArrangeSize.Height));
				}
			#endif
			return ArrangeSize;
		}

		protected override System.Windows.Size MeasureOverride(System.Windows.Size AvailableSize) {
			Size DesiredSize = new Size();

			#if PKSTACKPANEL_DEBUG
				if (this.InDebugNode || this.Name == DebugNodeName) {
					this.InDebugNode = true;
					Debug.WriteLine(String.Format("Measure({0}): AvailableSize: {1:#} x {2:#}", this.Name, AvailableSize.Width, AvailableSize.Height));
					Debug.Indent();
				}
			#endif

			foreach (FrameworkElement Child in this.InternalChildren) {
				Child.Measure(AvailableSize);

				switch (this.Orientation) {
					case Orientation.Horizontal:
                        DesiredSize.Width += Child.DesiredSize.Width;
						DesiredSize.Height = Math.Max(DesiredSize.Height, Child.DesiredSize.Height);
						break;
					case Orientation.Vertical:
						DesiredSize.Height += Child.DesiredSize.Height;
						DesiredSize.Width = Math.Max(DesiredSize.Width, Child.DesiredSize.Width);
						break;
				}
				#if PKSTACKPANEL_DEBUG
					if (this.InDebugNode) {
						Debug.WriteLine(String.Format("DesiredSize: {0:#} x {1:#}, {2}.DesiredSize: {3:#} x {4:#}", DesiredSize.Width, DesiredSize.Height, Child.Name, Child.DesiredSize.Width, Child.DesiredSize.Height));
					}
				#endif
			}

			#if PKSTACKPANEL_DEBUG
				if (this.InDebugNode) {
					Debug.Unindent();
					Debug.WriteLine(String.Format("~Measure({0}): DesiredSize: {1:#} x {2:#}", this.Name, DesiredSize.Width, DesiredSize.Height));
					if (this.Name == DebugNodeName)
						this.InDebugNode = false;
				}
			#endif

			return DesiredSize;
		}
	}
}
